#include "unp.h"

int daemon_proc = 0;

static void	err_doit(int, int, const char *, va_list);

int Accept(int fd, struct sockaddr *sa, socklen_t *salenptr)
{
 int n;

again:
  if((n = accept(fd, sa, salenptr)) < 0)
  {
  if(errno == EPROTO || errno == ECONNABORTED || errno == ENETDOWN || errno == ENOPROTOOPT || errno == EHOSTDOWN|| errno == ENONET || errno == EHOSTUNREACH || errno == EOPNOTSUPP || errno == EAGAIN || errno == ENETUNREACH)
    goto again;
  else
    err_sys("accept error");
  }
  return n;
}

void Bind(int fd, const struct sockaddr *sa, socklen_t salen)
{
  if(bind(fd, sa, salen) < 0)
    err_sys("bind error");
}

void Connect(int fd, const struct sockaddr *sa, socklen_t salen)
{
  if (TEMP_FAILURE_RETRY(connect(fd, sa, salen) < 0))
    err_sys("connect error");
}

void Listen(int fd, int backlog)
{
  char *ptr;

  /*4can override 2nd argument with environment variable */
  if((ptr = getenv("LISTENQ")) != NULL)
    backlog = atoi(ptr);

  if(listen(fd, backlog) < 0)
    err_sys("listen error");
}

int Poll(struct pollfd *fdarray, unsigned long nfds, int timeout)
{
  int n;

  if((n = poll(fdarray, nfds, timeout)) < 0)
    err_sys("poll error");

  return(n);
}

ssize_t Recv(int fd, void *ptr, size_t nbytes, int flags)
{
  ssize_t n;

  if((n = recv(fd, ptr, nbytes, flags)) < 0)
    err_sys("recv error");
  return(n);
}

ssize_t Recvfrom(int fd, void *ptr, size_t nbytes, int flags, struct sockaddr *sa, socklen_t *salenptr)
{
  ssize_t n;

  if((n = recvfrom(fd, ptr, nbytes, flags, sa, salenptr)) < 0)
    err_sys("recvfrom error");
  return(n);
}

int Select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)
{
  int n;

  if((n = select(nfds, readfds, writefds, exceptfds, timeout)) < 0)
    err_sys("select error");
  return(n);		/* can return 0 on timeout */
}

void Send(int fd, const void *ptr, size_t nbytes, int flags)
{
  if(TEMP_FAILURE_RETRY(send(fd, ptr, nbytes, flags)) != (ssize_t)nbytes)
    err_sys("send error");
}

void Sendto(int fd, const void *ptr, size_t nbytes, int flags, const struct sockaddr *sa, socklen_t salen)
{
  if(sendto(fd, ptr, nbytes, flags, sa, salen) != (ssize_t)nbytes)
    err_sys("sendto error");
}

void Setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)
{
  if(setsockopt(fd, level, optname, optval, optlen) < 0)
    err_sys("setsockopt error");
}

int Socket(int family, int type, int protocol)
{
  int n;

  if((n = socket(family, type, protocol)) < 0)
    err_sys("socket error");
  return(n);
}

void err_sys(const char *fmt, ...)
{
  va_list ap;

  va_start(ap, fmt);
  err_doit(1, LOG_ERR, fmt, ap);
  va_end(ap);
  exit(1);
}

static void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
  int errno_save, n;
  char buf[MAXLINE + 1];

  errno_save = errno;		/* value caller might want printed */
  vsnprintf(buf, MAXLINE, fmt, ap);	/* safe */
  n = strlen(buf);
  if(errnoflag)
    snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
  strcat(buf, "\n");

  if(daemon_proc)
  {
    syslog(level, buf);
  } 
  else 
  {
    fflush(stdout);		/* in case stdout and stderr are the same */
    fputs(buf, stderr);
    fflush(stderr);
  }
  return;
}

int Open(const char *pathname, int oflag, ...)
{
  int fd;

  if((fd = open(pathname, oflag)) == -1)
    err_sys("open error for %s", pathname);
  return(fd);
}

void Close(int fd)
{
  if (TEMP_FAILURE_RETRY(close(fd)) == -1)
    err_sys("close error");
}

ssize_t Read(int fd, void *ptr, size_t nbytes)
{
  ssize_t n;

  if((n = TEMP_FAILURE_RETRY(read(fd, ptr, nbytes))) == -1)
    err_sys("read error");
  return n;
}

void Write(int fd, void *ptr, size_t nbytes)
{
  if (write(fd, ptr, nbytes) != nbytes)
    err_sys("write error");
}

pid_t Waitpid(pid_t pid, int *iptr, int options)
{
  pid_t retpid;

  if((retpid = waitpid(pid, iptr, options)) == -1)
    err_sys("waitpid error");
  return(retpid);
}

pid_t Fork(void)
{
  pid_t pid;

  if((pid = fork()) == -1)
    err_sys("fork error");
  return(pid);
}

int Fcntl(int fd, int cmd, int arg)
{
  int n;

  if((n = fcntl(fd, cmd, arg)) == -1)
    err_sys("fcntl error");
  return(n);
}

ssize_t writen(int fd, const void *vptr, size_t n)
{
  size_t nleft;
  ssize_t nwritten;
  const char *ptr;

  ptr = (char*)vptr;
  nleft = n;
  while(nleft > 0) 
  {
    if((nwritten = TEMP_FAILURE_RETRY(write(fd, ptr, nleft))) < 0) 
			return EXIT_FAILURE;

    nleft -= nwritten;
    ptr   += nwritten;
  }
  return n;
}

void Writen(int fd, void *ptr, size_t nbytes)
{
  if (writen(fd, ptr, nbytes) != nbytes)
    err_sys("writen error");
}

int udp_client(const char *host, const char *serv, SA **saptr, socklen_t *lenp)
{
  int sockfd, n;
  struct addrinfo hints, *res, *ressave;

  bzero(&hints, sizeof(struct addrinfo));
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;

  if((n = getaddrinfo(host, serv, &hints, &res)) != 0)
    err_quit("udp_client error for %s, %s: %s", host, serv, gai_strerror(n));
  ressave = res;

  do 
  {
    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if (sockfd >= 0)
      break;		/* success */
  }while((res = res->ai_next) != NULL);

  if (res == NULL)	/* errno set from final socket() */
    err_sys("udp_client error for %s, %s", host, serv);

  *saptr = (SA*)Malloc(res->ai_addrlen);
  memcpy(*saptr, res->ai_addr, res->ai_addrlen);
  *lenp = res->ai_addrlen;

  freeaddrinfo(ressave);

  return(sockfd);
}
/* end udp_client */

int Udp_client(const char *host, const char *serv, SA **saptr, socklen_t *lenptr)
{
  return(udp_client(host, serv, saptr, lenptr));
}

int udp_server(const char *host, const char *serv, socklen_t *addrlenp)
{
  int sockfd, n;
  struct addrinfo	hints, *res, *ressave;

  bzero(&hints, sizeof(struct addrinfo));
  hints.ai_flags = AI_PASSIVE;
  hints.ai_family = AF_UNSPEC;
  hints.ai_socktype = SOCK_DGRAM;

  if((n = getaddrinfo(host, serv, &hints, &res)) != 0)
    err_quit("udp_server error for %s, %s: %s", host, serv, gai_strerror(n));
  ressave = res;

  do 
  {
    sockfd = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
    if(sockfd < 0)
      continue;		/* error - try next one */

    if (bind(sockfd, res->ai_addr, res->ai_addrlen) == 0)
      break;			/* success */

    Close(sockfd);		/* bind error - close and try next one */
  } while((res = res->ai_next) != NULL);

  if(res == NULL)	/* errno from final socket() or bind() */
    err_sys("udp_server error for %s, %s", host, serv);

  if(addrlenp)
    *addrlenp = res->ai_addrlen;	/* return size of protocol address */

  freeaddrinfo(ressave);

  return(sockfd);
}
/* end udp_server */

int Udp_server(const char *host, const char *serv, socklen_t *addrlenp)
{
  return(udp_server(host, serv, addrlenp));
}

void err_quit(const char *fmt, ...)
{
  va_list ap;

  va_start(ap, fmt);
  err_doit(0, LOG_ERR, fmt, ap);
  va_end(ap);
  exit(1);
}

void * Malloc(size_t size)
{
  void *ptr;

  if((ptr = malloc(size)) == NULL)
    err_sys("malloc error");
  return(ptr);
}

ssize_t readn(int fd, void *vptr, size_t n)
{
  size_t nleft;
  ssize_t nread;
  char *ptr;

  ptr = (char *)vptr;
  nleft = n;
  while (nleft > 0)
  {
    if((nread = TEMP_FAILURE_RETRY(read(fd, ptr, nleft))) < 0) 
    	return EXIT_FAILURE;
    else if(nread == 0)
      break;				/* EOF */

    nleft -= nread;
    ptr += nread;
  }
  return(n - nleft);		/* return >= 0 */
}
/* end readn */

ssize_t Readn(int fd, void *ptr, size_t nbytes)
{
  ssize_t n;

  if((n = readn(fd, ptr, nbytes)) < 0)
    err_sys("readn error");
  return(n);
}

void Pthread_create(pthread_t *tid, const pthread_attr_t *attr, void * (*func)(void *), void *arg)
{
  int n;

  if((n = pthread_create(tid, attr, func, arg)) == 0)
    return;
  errno = n;
  err_sys("pthread_create error");
}

void Pthread_join(pthread_t tid, void **status)
{
  int n;

  if((n = pthread_join(tid, status)) == 0)
    return;
  errno = n;
  err_sys("pthread_join error");
}

void Pthread_detach(pthread_t tid)
{
  int n;

  if((n = pthread_detach(tid)) == 0)
    return;
  errno = n;
  err_sys("pthread_detach error");
}

void Pthread_kill(pthread_t tid, int signo)
{
  int n;

  if((n = pthread_kill(tid, signo)) == 0)
    return;
  errno = n;
  err_sys("pthread_kill error");
}

void Pthread_mutexattr_init(pthread_mutexattr_t *attr)
{
  int n;

  if((n = pthread_mutexattr_init(attr)) == 0)
    return;
  errno = n;
  err_sys("pthread_mutexattr_init error");
}

#ifdef	_POSIX_THREAD_PROCESS_SHARED
void Pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int flag)
{
  int n;

  if((n = pthread_mutexattr_setpshared(attr, flag)) == 0)
    return;
  errno = n;
  err_sys("pthread_mutexattr_setpshared error");
}
#endif

void Pthread_mutex_init(pthread_mutex_t *mptr, pthread_mutexattr_t *attr)
{
  int n;

  if((n = pthread_mutex_init(mptr, attr)) == 0)
    return;
  errno = n;
  err_sys("pthread_mutex_init error");
}

/* include Pthread_mutex_lock */
void Pthread_mutex_lock(pthread_mutex_t *mptr)
{
  int n;

  if((n = pthread_mutex_lock(mptr)) == 0)
    return;
  errno = n;
  err_sys("pthread_mutex_lock error");
}
/* end Pthread_mutex_lock */

void Pthread_mutex_unlock(pthread_mutex_t *mptr)
{
  int n;

  if((n = pthread_mutex_unlock(mptr)) == 0)
    return;
  errno = n;
  err_sys("pthread_mutex_unlock error");
}

void Pthread_cond_broadcast(pthread_cond_t *cptr)
{
  int n;

  if((n = pthread_cond_broadcast(cptr)) == 0)
    return;
  errno = n;
  err_sys("pthread_cond_broadcast error");
}

void Pthread_cond_signal(pthread_cond_t *cptr)
{
  int n;

  if((n = pthread_cond_signal(cptr)) == 0)
    return;
  errno = n;
  err_sys("pthread_cond_signal error");
}

void Pthread_cond_wait(pthread_cond_t *cptr, pthread_mutex_t *mptr)
{
  int n;

  if((n = pthread_cond_wait(cptr, mptr)) == 0)
    return;
  errno = n;
  err_sys("pthread_cond_wait error");
}

void Pthread_cond_timedwait(pthread_cond_t *cptr, pthread_mutex_t *mptr, const struct timespec *tsptr)
{
  int n;

  if((n = pthread_cond_timedwait(cptr, mptr, tsptr)) == 0)
    return;
  errno = n;
  err_sys("pthread_cond_timedwait error");
}

void Pthread_once(pthread_once_t *ptr, void (*func)(void))
{
  int n;

  if((n = pthread_once(ptr, func)) == 0)
    return;
  errno = n;
  err_sys("pthread_once error");
}

void Pthread_key_create(pthread_key_t *key, void (*func)(void *))
{
  int n;

  if((n = pthread_key_create(key, func)) == 0)
    return;
  errno = n;
  err_sys("pthread_key_create error");
}

void Pthread_setspecific(pthread_key_t key, const void *value)
{
  int n;

  if((n = pthread_setspecific(key, value)) == 0)
    return;
  errno = n;
  err_sys("pthread_setspecific error");
}
